Xceed Zip Compression Library Documentation
Compress method example for VC++
Example topics > Compress method example for VC++

This example shows the use of the Compress and Uncompress methods in C++. The example focuses on the handling of the VARIANT data structure on a low-level so you can see how the magic works. The example also shows how different types of data can be compressed and uncompressed, namely strings, unicode strings and raw data.

The example code below is ready to compile and run into a console C++ application.

VC++ Copy Code

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>

#import "XceedZip.dll" named_guids no_namespace

template< class DataType > static void PrintByteArrayVariant( VARIANT * variant );
static void PrintVariantAsString( VARIANT * variant );
static void PrintVariantAsUnicodeString( VARIANT * variant );

static void StringExample( IXceedCompressionPtr & compression, const char * data )
{
  xcdCompressionError error;
  VARIANT dataToCompress;
  VARIANT compressedData;
  VARIANT uncompressedData;
  SAFEARRAY * safeArray;
  char * safeArrayData;
  size_t length;

  /* The VARIANT structure doesn't directly support 8-bit strings. We will
     have to supply the string as a byte array.
    
     We will not compress the terminating null character. */

  // Compute the length of the string
  length = strlen( data );

  // Create a one-dimensional array using the length (excluding the null character)
  safeArray = SafeArrayCreateVector( VT_I1, 0, length );
 
  // Access the array data and copy the string to the array
  SafeArrayAccessData( safeArray, ( void ** ) &safeArrayData );
  CopyMemory( safeArrayData, data, length );
  SafeArrayUnaccessData( safeArray );

  // Initialize the variants we will use
  VariantInit( &dataToCompress );
  VariantInit( &compressedData );
  VariantInit( &uncompressedData );

  // Set the data type to an array of bytes
  dataToCompress.vt = VT_ARRAY | VT_I1;
 
  // Assign the safe array to the VARIANT. Responsability is transfered
  dataToCompress.parray = safeArray;

  // Compress the data
  error = compression->Compress( &dataToCompress, &compressedData, VARIANT_TRUE );

  // We no longer need the data to compress
  VariantClear( &dataToCompress );

  if ( error != xceSuccess )
    throw new _com_error( error );

  /* compressedData now contains an array of bytes. This is not a string. It is the
     compressed data and is not in a specific form.

     In this example, we will display each byte in the array as an hexadecimal value.
     In a real application, the data in the array could be saved in a file, sent to another
     computer, whatever. */

  printf( "Begin compressed data:\n" );
  PrintByteArrayVariant< unsigned char >( &compressedData );
  printf( "\nEnd compressed data.\n\n" );

  // Uncompress the data
  error = compression->Uncompress( &compressedData, &uncompressedData, VARIANT_TRUE );

  // We no longer need the compressed data
  VariantClear( &compressedData );

  if ( error != xceSuccess )
    throw new _com_error( error );

  printf( "Begin uncompressed data:\n" );
  PrintVariantAsString( &uncompressedData );
  printf( "\nEnd uncompressed data.\n\n" );

  // Clear the uncompressed data
  VariantClear( &uncompressedData );
}

static void UnicodeStringExample( IXceedCompressionPtr & compression, const wchar_t * data )
{
  xcdCompressionError error;
  VARIANT dataToCompress;
  VARIANT compressedData;
  VARIANT uncompressedData;
  _bstr_t bstring;

  /* The VARIANT structure doesn't support straight unicode strings. But it does
     support something close to it. Something called BSTR.

     A BSTR, known as basic string or binary string, is a pointer to a wide
     character string used by Automation data manipulation functions.

     We will use the COM _bstr_t helper class to build the BSTR out of the
     supplied unicode string. */

  // Copy the supplied unicode string data to the BSTR object
  bstring = data;

  // Initialize the variants we will use
  VariantInit( &dataToCompress );
  VariantInit( &compressedData );
  VariantInit( &uncompressedData );

  // Set the data type to BSTR
  dataToCompress.vt = VT_BSTR;
 
  // Assign the safe array to the VARIANT. Responsability is transfered
  dataToCompress.bstrVal = bstring;

  // Compress the data
  error = compression->Compress( &dataToCompress, &compressedData, VARIANT_TRUE );

  // We no longer need the data to compress
  VariantClear( &dataToCompress );

  if ( error != xceSuccess )
    throw new _com_error( error );

  /* compressedData now contains an array of bytes. This is not a string. It is the
     compressed data and is not in a specific form.

     In this example, we will display each byte in the array as an hexadecimal value.
     In a real application, the data in the array could be saved in a file, sent to another
     computer, whatever. */

  printf( "Begin compressed data:\n" );
  PrintByteArrayVariant< unsigned char >( &compressedData );
  printf( "\nEnd compressed data.\n\n" );

  // Uncompress the data
  error = compression->Uncompress( &compressedData, &uncompressedData, VARIANT_TRUE );

  // We no longer need the compressed data
  VariantClear( &compressedData );

  if ( error != xceSuccess )
    throw new _com_error( error );

  printf( "Begin uncompressed data:\n" );
  PrintVariantAsUnicodeString( &uncompressedData );
  printf( "\nEnd uncompressed data.\n\n" );

  // Clear the uncompressed data
  VariantClear( &uncompressedData );
}

static void RawDataExample( IXceedCompressionPtr & compression, unsigned long * data, int count )
{
  xcdCompressionError error;
  VARIANT dataToCompress;
  VARIANT compressedData;
  VARIANT uncompressedData;
  SAFEARRAY * safeArray;
  unsigned long * safeArrayData;
  size_t size;

  /* Although the VARIANT structure supports 4 byte unsigned integers, the Compress
     function only accepts BSTR or byte arrays in its VARIANT source parameter.
    
     We will therefore have to supply the data as a byte array. */

  // Compute the size in bytes of the supplied data
  size = count * sizeof( unsigned long );

  // Create a one-dimensional array of unsigned bytes
  safeArray = SafeArrayCreateVector( VT_UI1, 0, size );
 
  // Access the array data and copy the data to the array
  SafeArrayAccessData( safeArray, ( void ** ) &safeArrayData );
  CopyMemory( safeArrayData, data, size );
  SafeArrayUnaccessData( safeArray );

  // Initialize the variants we will use
  VariantInit( &dataToCompress );
  VariantInit( &compressedData );
  VariantInit( &uncompressedData );

  // Set the data type to an array of unsigned bytes
  dataToCompress.vt = VT_ARRAY | VT_UI1;
 
  // Assign the safe array to the VARIANT. Responsability is transfered
  dataToCompress.parray = safeArray;

  // Compress the data
  error = compression->Compress( &dataToCompress, &compressedData, VARIANT_TRUE );

  // We no longer need the data to compress
  VariantClear( &dataToCompress );

  if ( error != xceSuccess )
    throw new _com_error( error );

  /* compressedData now contains an array of bytes. This is not a string. It is the
     compressed data and is not in a specific form.

     In this example, we will display each byte in the array as an hexadecimal value.
     In a real application, the data in the array could be saved in a file, sent to another
     computer, whatever. */

  printf( "Begin compressed data:\n" );
  PrintByteArrayVariant< unsigned char >( &compressedData );
  printf( "\nEnd compressed data.\n\n" );

  // Uncompress the data
  error = compression->Uncompress( &compressedData, &uncompressedData, VARIANT_TRUE );

  // We no longer need the compressed data
  VariantClear( &compressedData );

  if ( error != xceSuccess )
    throw new _com_error( error );

  printf( "Begin uncompressed data:\n" );
  PrintByteArrayVariant< unsigned long >( &uncompressedData );
  printf( "\nEnd uncompressed data.\n\n" );

  // Clear the uncompressed data
  VariantClear( &uncompressedData );
}

template< class DataType > static void PrintByteArrayVariant( VARIANT * variant )
{
  DataType * data;
  SAFEARRAY * arrayDescriptor;
  int i, count;

  // Get the array descriptor from the variant
  arrayDescriptor = variant->parray;

  // Gain access to the array data
  SafeArrayAccessData( arrayDescriptor, ( void ** ) &data );

  // Get the number of elements in the first dimension of the array
  count = arrayDescriptor->rgsabound[ 0 ].cElements / sizeof( DataType );

  int lineThreshold = 32 / sizeof( DataType );
  int groupThreshold = 4 / sizeof( DataType );
  char buffer[ 16 ];

  sprintf( buffer, "%%0%dX", sizeof( DataType ) * 2 );
 
  for( i = 0; i < count; i++ )
  {
    // If we need to change line
    if( ( i % lineThreshold ) == 0 && i > 0 )
      printf( "\n" );

    // If we need to seperate a group of data
    if( ( i % groupThreshold ) == 0 )
      printf( " " );

    // Access the current index as we would a normal array
    printf( buffer, data[ i ] );
  }

  // Release access to the array data
  SafeArrayUnaccessData( arrayDescriptor );
}

static void PrintVariantAsString( VARIANT * variant )
{
  char * string;
  SAFEARRAY * arrayDescriptor;
  int i, count;

  // Get the array descriptor from the variant
  arrayDescriptor = variant->parray;

  // Gain access to the array data
  SafeArrayAccessData( arrayDescriptor, ( void ** ) &string );

  // Get the number of elements in the first dimension of the array
  count = arrayDescriptor->rgsabound[ 0 ].cElements;

  /* Since the uncompressed data doesn't contain the terminating null character
     we either have to print the string one character at a time knowing the
     lenght of the array. Or, we can copy the data to another buffer that has the
     size to contain the null character.

     Here, we choose to print one character at a time. */
 
  for( i = 0; i < count; i++ )
  {
    // Print the current character
    printf( "%c", string[ i ] );
  }

  // Release access to the array data
  SafeArrayUnaccessData( arrayDescriptor );
}

static void PrintVariantAsUnicodeString( VARIANT * variant )
{
  HRESULT result;

  /* We need to convert the variant type from whatever (usually byte array in
     our examples) to BSTR. We will show how this is done when no helper classes
     are used.
    
     The VariantChangeType function does this. We will attempt to make this
     conversion in place (meaning to replace the current data with the converted
     data. */

  // Convert the variant data to a BSTR
  result = VariantChangeType( variant, variant, 0, VT_BSTR );

  // If the conversion was successful
  if( result == S_OK )
  {
    /* We will use the COM BSTR wrapper class to easily access the actual
       string. But we will NOT copy the string data */

    // Wrap the BSTR without copying the data
    _bstr_t bString( variant->bstrVal, false );

    // BSTR strings are unicode
    const wchar_t * actualString = bString;

    // Print the unicode string
    wprintf( L"%s", actualString );
  }
}

int main( int argc, char * argv[] )
{
  CoInitialize( NULL );

  try
  {
    char textData[] =
      "It's actually 607 small islands in the South Pacific. "
      "Interestingly, while its total land mass is only 270 square miles, "
      "it occupies more than a million square miles of the Pacific Ocean. "
      "Population is 127,000 and the U.S. Embassy is located in the state of "
      "Pohnpei and not, as many people believe, on the island of Yap.";

    wchar_t unicodeTextData[] =
      L"You need to listen to me. You have to listen to me. I can't help you, unless you listen "
      L"to me! You can't send Christmas cards to everyone, you can't do it! Forget the "
      L"SPR, let's get the IMF loans like we said we were going to, listen to what I have to say "
      L"about Didion, and please, listen to me!";

    unsigned long rawData[] = { 0xFEEEEEEE, 0x80018001, 0x00000000, 0xFFFFFFFF, 0x36FE0006 };

    // Create the XceedCompression object
    IXceedCompressionPtr compression( CLSID_XceedCompression );
   
    // License the object
    if( compression->License( _bstr_t( L"your license key" ) ) == VARIANT_FALSE )
    {
      printf( "License() failed.\n" );
      return 1;
    }

    /* There are several ways to manipulate VARIANT data. In this example, we will
       try to show how VARIANT data is handled without 'magic' so we will use the VARIANT
       structure directly.

       The COM helper class _variant_t encapsulates a VARIANT structure and provides
       useful methods to work with it.

       In MFC, the COleVariant class also encapsulates the VARIANT structure.

       All the operations done on VARIANTs in these examples can be seen as simple
       methods in the _variant_t or COleVariant classes.

       In order to keep this example from becoming too heavy, we will use the COM
       helper class _bstr_t that provides a useful wrapper around the BSTR data type. */
  
    // A compression/decompression example using a string as data
    StringExample( compression, textData );

    // A compression/decompression example using a unicode string as data
    UnicodeStringExample( compression, unicodeTextData );

    // A compression/decompression example using raw data
    RawDataExample( compression, rawData, sizeof( rawData ) / sizeof( unsigned long ) );
  }
  catch( const _com_error& error )
  {
    printf( "COM error %08x. %S\n", error.Error(), ( const char* ) error.Description() );
  }
  catch( ... )
  {
    printf( "Unexpected error\n" );
  }

  CoUninitialize();
  return 0;
}